#![allow(non_snake_case)]
use std::env::args;
use std::fs;
use std::io::Write;
use std::process::exit;
#[macro_use]
mod macros;
#[derive(Debug)]
struct BrackParse(usize, usize);
//             |brack1  |brack2  |file_point
struct FileParserAnnotation;
impl FileParserAnnotation {
    fn Main(file: String) -> String {
        let mut result = String::new();
        'a: for i in file.split("\n") {
            if i.as_bytes()[0] == '@' as u8 {
                result.push_str(format!("{}\n", i).as_str());
            }
            for one_char in i.as_bytes() {
                if *one_char == ';' as u8 {
                    continue 'a;
                } else if *one_char == '@' as u8 {
                    continue 'a;
                } else {
                    result.push(*one_char as char);
                }
            }
        }
        result = Self::ParsingMacros(result);
        result
    }
    fn checking_file_syntax(file: &String, file_name: &str) {
        let mut stack: Vec<usize> = Vec::new();
        for (i, value) in file.as_bytes().iter().enumerate() {
            match *value as char {
                '[' => stack.push(i),
                ']' => {
                    stack.pop();
                }
                _ => (),
            }
        }
        if stack.is_empty() {
            return;
        } else {
            println!("\x1b[31;1mEEROR\x1b[0m");
            println!("File index(s):{:?}", stack);
            println!(
                "Message: Brack parse ERROR at function checking_file\nwith file:{}",
                file_name
            );
            exit(1);
        }
    }
    fn ParsingMacros(file: String) -> String {
        /*
                [main.bf]
                ++++++++++++++++++++++++++++++++++++++++++++++++++.
                @file hello.bf
        -------------------------------------------------------------------
                [hello.bf]
                +++++++++>.
                */
        let mut result = file.clone();
        'line_iter: for (index, oneline) in file.split("\n").enumerate() {
            if oneline.as_bytes()[0] == '@' as u8 {
                let symbols: Vec<&str> = oneline.split_whitespace().collect();
                //match macros error
                if symbols.len() != 2 {
                    err!("parsing macros", index)
                }
                match symbols[0] {
                    "@file" => {
                        let tmp_file = fs::read_to_string(symbols[1].trim()).unwrap_or_else(|e| {
                            err!(format!("{} with {}", e, symbols[1]), index);
                        });
                        Self::checking_file_syntax(&tmp_file, symbols[1]);
                        result = result.replace(oneline, &tmp_file);
                    }
                    _ => (),
                }
            } else {
                continue 'line_iter;
            }
        }
        result
    }
}
impl BrackParse {
    fn new(file: &String) -> Vec<Self> {
        let mut tmp_track: Vec<usize> = Vec::new();
        let mut result: Vec<Self> = Vec::new();
        let file = file.as_bytes();
        for i in 0..file.len() {
            let tmp = file[i] as char;
            if tmp == '[' {
                tmp_track.push(i)
            } else if tmp == ']' {
                result.push(Self(
                    tmp_track.pop().unwrap_or_else(|| {
                        println!("\x1b[31;1mEEROR\x1b[0m");
                        println!("File index:{}", i + 1);
                        println!("Message: Brack parse ERROR at function parsing_file");
                        exit(1);
                    }),
                    i,
                ));
            }
        }
        result
    }
}
impl BrackParse {
    fn find_other_one_in_vec(vec: &Vec<Self>, one: usize) -> usize {
        let mut result: usize = 0;
        let mut is_error = true;
        for a in vec {
            if one == a.0 {
                result = a.1;
                is_error = false;
            } else if one == a.1 {
                result = a.0;
                is_error = false;
            }
        }
        if is_error {
            println!("\x1b[31;1mEEROR\x1b[0m");
            println!("File index:known");
            println!("Message: Brack parse ERROR at function runing");
            exit(1);
        }
        result
    }
}
#[derive(Debug)]
struct Stack {
    stack: Vec<char>,
    pointer: usize,
    file_pointer: usize,
    bracks: Vec<BrackParse>, //0 is 1
}
impl Stack {
    fn err(&self, s: &str) -> ! {
        println!("\x1b[31;1mEEROR\x1b[0m");
        println!("File index:{}", self.file_pointer + 1);
        println!("Message: {}", s);
        exit(1);
    }
    fn pointer_add(&mut self, sum: isize) {
        let a = self.pointer as isize + sum;
        if a < 0 {
            return;
        }
        self.pointer = a as usize;
    }
    fn get_value_by_pointer(&self) -> char {
        match self.stack.get(self.pointer as usize) {
            Some(s) => *s,
            None => self.err("read from stack error"),
        }
    }
    fn self_a_be_pointed_anrry(&mut self, a: isize) {
        //println!("{:?}",self);
        let tmp = self.stack[self.pointer];
        self.stack[self.pointer] = (tmp as u8 as isize + a) as u8 as char
    }
    fn input_com(&mut self) {
        let mut buf = String::new();
        std::io::stdin().read_line(&mut buf).unwrap();
        let intochar = buf.as_bytes()[0];
        self.stack[self.pointer] = intochar as char;
    }
    fn output_com(&self) {
        print!("{}", self.stack[self.pointer]);
        std::io::stdout().flush().unwrap();
    }
    fn if_pointer_is_zero_then_jump_two(&mut self) {
        if self.stack[self.pointer] == 0 as char {
            self.file_pointer = BrackParse::find_other_one_in_vec(&self.bracks, self.file_pointer);
            //use find_other_one_in_vec
        }
    }
    fn if_pointer_is_not_zero_then_jump_one(&mut self) {
        if self.stack[self.pointer] != 0 as char {
            self.file_pointer = BrackParse::find_other_one_in_vec(&self.bracks, self.file_pointer)
        }
    }
    fn run(&mut self, com: char) {
        match com {
            '>' => {
                self.stack.push(0 as char);
                self.pointer_add(1);
            }
            //the pointer add one
            '<' => {
                self.pointer_add(-1);
            }
            //the pointer minus one
            '+' => self.self_a_be_pointed_anrry(1),
            '-' => self.self_a_be_pointed_anrry(-1),
            ',' => self.input_com(),
            '.' => self.output_com(),
            '[' => self.if_pointer_is_zero_then_jump_two(),
            ']' => self.if_pointer_is_not_zero_then_jump_one(),
            '\n' => (),
            ' ' => (),
            _ => self.err("known symbol"),
        }
    }
    fn run_main(&mut self, file: String) {
        let file = file.as_bytes();
        loop {
            if self.file_pointer >= file.len() {
                break;
            }
            self.run(file[self.file_pointer] as char);
            //self can edit the file pointer
            self.file_pointer += 1;
        }
    }
}
fn help() -> ! {
    let helps = "
A interpreter of BrainFuck
    $ BF (File Name) [Other args]
";
    println!("{}", helps);
    exit(0);
}
fn main() {
    let args: Vec<String> = args().collect();
    let file_name = match args.get(1) {
        Some(v) => v,
        None => help(),
    };
    let file = fs::read_to_string(file_name).unwrap_or_else(|e| {
        println!("{}", e);
        exit(1);
    });
    let file = FileParserAnnotation::Main(file);
    let mut BF = Stack {
        stack: Vec::new(),
        pointer: 0,
        file_pointer: 0,
        bracks: BrackParse::new(&file),
    };
    BF.stack.push(0 as char);
    BF.run_main(file);
}
